package org.lisy.java.thread;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicLongArray;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReferenceArray;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.concurrent.atomic.AtomicStampedReference;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.IntBinaryOperator;
import java.util.function.IntUnaryOperator;

/**
 * 原子操作类
 * 
 * @author lisy
 */
public class AtomicOperate {

	public static void main(String[] args) {
		
		/**
		 * 基本类型
		 * 1. AtomicBoolean:原子更新布尔类型
		 * 2. AtomicInteger:原子更新整型
		 * 3. AtomicLong: 原子更新长整型
		 */
		basicType();
		
		/**
		 * 引用类型
		 * 1. AtomicReference: 利用 CAS 来更新引用，旧值为原来的引用对象，新值为新的引用对象;
		 *    缺点:不能解决 ABA 问题。ABA 问题就是多个线程前后修改值，导致线程 CAS 前后值没有变化，但是中间却发生了修改
		 * 2. AtomicStampedReference: 通过引入时间戳来解决了 ABA 问题。每次要更新值的时候，需要额外传入 oldStamp 和 newStamp。
		 *    将对象和 stamp(版本)包装成了一个 Pair 对象(静态内部类)
		 * 3. AtomicMarkableReference: 跟 AtomicStampedReference 的结构类似，包装了一个 boolean 变量，而不是 int 变量。
		 *    无法解决 ABA 问题的，因为 boolean 变量的 mark 是有很大可能重合的，还是会导致更新成功
		 */
		referenceType();
		
		/**
		 * 数组
		 * 1. AtomicIntegerArray: 原子更新整形数组
		 * 2. AtomicLongArray: 原子更新长整型数组
		 * 3. AtomicReferenceArray: 原子更新引用类型数组里的元素
		 */
		arrayType();
		
		/**
		 * 属性
		 * 1. AtomicIntegerFieldUpdater: 原子更新整形属性
		 * 2. AtomicLongFieldUpdater: 原子更新长整形属性
		 * 3. AtomicReferenceFieldUpdater: 原子更新引用类型属性
		 * 注意: 更改的属性必须用 public volatile 修饰，否则运行时会抛出 IllegalArgumentException
		 */
		fieldType();
		
		/**
		 * LongAdder 内部用数组做了分段锁
		 * 高并发下效率: LongAdder > Atomic > syncronized
		 */
		longAdderMethod();
	}

	private static void basicType() {
		System.out.println("========== basic type ==========");
		AtomicInteger atomicInteger = new AtomicInteger(123);
		System.out.println(atomicInteger.get());
		// 原子化的 ++i
		System.out.println(atomicInteger.incrementAndGet());
		// 原子化 i++
		System.out.println(atomicInteger.getAndIncrement());
		System.out.println(atomicInteger.get());
		
		// 原子化的 --i
		System.out.println(atomicInteger.decrementAndGet());
		// 原子化的 i--
		System.out.println(atomicInteger.getAndDecrement());
		System.out.println(atomicInteger.get());
		
		// 当前值 += delta，返回 += 后的值
		System.out.println(atomicInteger.addAndGet(7));
		// 当前值 += delta，返回 += 前的值
		System.out.println(atomicInteger.getAndAdd(7));
		System.out.println(atomicInteger.get());
		
		// 更新
		System.out.println(atomicInteger.getAndSet(200));
		System.out.println(atomicInteger.get());
		atomicInteger.lazySet(300);
		System.out.println(atomicInteger.get());
		atomicInteger.set(100);
		System.out.println(atomicInteger.get());
		
		// CAS 操作，返回是否成功
		System.out.println(atomicInteger.compareAndSet(atomicInteger.get(), 100));
		System.out.println(atomicInteger.get());
		
		// 新值可以通过传入 func 函数来计算
		IntUnaryOperator unaryOperator = (x) -> - x;
		System.out.println(atomicInteger.updateAndGet(unaryOperator));
		System.out.println(atomicInteger.getAndUpdate(unaryOperator));
		System.out.println(atomicInteger.get());
		
		IntBinaryOperator add = (u, v) -> Integer.valueOf(String.valueOf(u) + String.valueOf(v));
		System.out.println(atomicInteger.accumulateAndGet(200, add));
		System.out.println(atomicInteger.getAndAccumulate(200, add));
		System.out.println(atomicInteger.get());
	}
	
	private static void referenceType() {
		System.out.println("========== reference type ==========");
		
		AtomicUser user = new AtomicUser("lisy", 27);
		AtomicReference<AtomicUser> userAtomicReference = new AtomicReference<>(user);
		while (true){
			AtomicUser updateUser = new AtomicUser("li", 20);
		    boolean flag = userAtomicReference.compareAndSet(userAtomicReference.get(), updateUser);
		    if (flag){
		        System.out.println(userAtomicReference.get());
		        break;
		    }
		}
		
		AtomicUser t_user = new AtomicUser("lisy", 18);
		AtomicStampedReference<AtomicUser> userAtomicStampedReference = new AtomicStampedReference<>(t_user, 1);
		while (true){
			AtomicUser updateUser = new AtomicUser("li", 22);
		    userAtomicStampedReference.getStamp();
		    int[] stamp = new int[1];
		    AtomicUser oldUser = userAtomicStampedReference.get(stamp);
		    boolean flag = userAtomicStampedReference.compareAndSet(oldUser, updateUser, stamp[0], stamp[0] + 1);
		    if (flag){
		        break;
		    }
		}
		int[] s = new int[1];
		System.out.println(userAtomicStampedReference.get(s));
		System.out.println(s[0]);
	}

	private static void arrayType() {
		System.out.println("========== array type ==========");
		
		AtomicLongArray longArray = new AtomicLongArray(10);
		while (true){
		    boolean flag = longArray.compareAndSet(0, longArray.get(0), 2);
		    if (flag){
		        System.out.println(longArray.get(0) + "---" + flag);
		        break;
		    }
		}

		AtomicReferenceArray<AtomicUser> referenceArray = new AtomicReferenceArray<>(10);
		while (true){
		    boolean flag = referenceArray.compareAndSet(0, referenceArray.get(0), new AtomicUser("lisy", 21));
		    if (flag){
		        System.out.println(referenceArray.get(0) + "---" + flag);
		        break;
		    }
		}
	}

	private static void fieldType() {
		System.out.println("========== field type ==========");
		
		AtomicUser user = new AtomicUser("lisy", 25);
		AtomicReferenceFieldUpdater<AtomicUser, String> fieldUpdater = AtomicReferenceFieldUpdater.newUpdater(AtomicUser.class, String.class, "name");
		AtomicIntegerFieldUpdater<AtomicUser> integerFieldUpdater = AtomicIntegerFieldUpdater.newUpdater(AtomicUser.class, "age");
		fieldUpdater.compareAndSet(user, user.getName(), "hahaha");
		integerFieldUpdater.compareAndSet(user, user.getAge(), 100);
		System.out.println(user);
	}
	
	private static void longAdderMethod() {
		System.out.println("========== LongAdder ==========");
		LongAdder count = new LongAdder();
		System.out.println(count);
		count.increment();
		// 高并发情况，sum()方法返回值并不完全精确
		System.out.println(count.sum());	
	}
}

class AtomicUser {
	public volatile String name;
	public volatile int age;
	
	public AtomicUser(String name, int age) {
		this.name = name;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "AtomicUser [name=" + name + ", age=" + age + "]";
	}
}

